#ifndef __BLOCK_QUEUE_HPP__
#define __BLOCK_QUEUE_HPP__

#include<iostream>
#include<string>
#include<queue>
#include<pthread.h>

template<class T>
class BlockQueue
{
private:
    //判断阻塞队列（即缓冲区）是否为满
    bool IsFull()
    {
        return _block_queue.size()==_cap;
    }
    //判断阻塞队列是否为空
    bool IsEmpty()
    {
        return _block_queue.empty();
    }
public:
    BlockQueue(int cap)
        :_cap(cap)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_consumer_cond,nullptr);
        pthread_cond_init(&_productor_cond,nullptr);
    }

    //生产者放入数据
    void Enqueue(T &in)
    {
        pthread_mutex_lock(&_mutex);

        //阻塞队列满了，生产者线程等待
        while(IsFull())
        {
            _productor_wait_num++;//等待的数量+1
            pthread_cond_wait(&_productor_cond,&_mutex);
            _productor_wait_num--;//等待的数量-1
        }
        //阻塞队列不是满的才可以放入数据
        _block_queue.push(in);

        //通知消费者线程，可以消费了
        //有消费者线程在等待才唤醒
        if(_consumer_wait_num>0)
            pthread_cond_signal(&_consumer_cond);

        pthread_mutex_unlock(&_mutex);
    }
    //消费者取出数据，out是输出型参数
    void Pop(T *out)
    {
        pthread_mutex_lock(&_mutex);

        //阻塞队列为空，消费者线程等待
        while(IsEmpty())
        {
            _consumer_wait_num++;
            pthread_cond_wait(&_consumer_cond,&_mutex);
            _consumer_wait_num--;
        }
        //阻塞队列不为空，消费者可以取出数据
        *out=_block_queue.front();
        _block_queue.pop();

        //如果生产者线程因为阻塞队列满了在等待，则唤醒生产者线程
        if(_productor_wait_num>0)
            pthread_cond_signal(&_productor_cond);

        pthread_mutex_unlock(&_mutex);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_consumer_cond);
        pthread_cond_destroy(&_productor_cond);
    }

private:
    std::queue<T> _block_queue;
    int _cap;//上限
    pthread_mutex_t _mutex;//互斥锁
    pthread_cond_t _consumer_cond;//消费者的条件变量
    pthread_cond_t _productor_cond;//生产者的条件变量

    int _consumer_wait_num;//生产者在等待队列中的数量
    int _productor_wait_num;//消费者在等待队列中的数量
};

#endif